/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ola_coap.h"
#include <stdint.h>
#include "securec.h"
#include "ola_error.h"
#include "ola_log.h"

static int CoapParseHeaderForUdp(OlaCoapPacket *pkt, const OlaData *data)
{
    if (data->len < OLA_COAP_UDP_HEADER_LEN) {
        OlaLogWarning("header len invalid");
        return OLA_COAP_ERR_HEADER_TOO_SHORT;
    }

    /* 报文第0个字节的高2位为版本号 */
    pkt->header.ver = (((unsigned int)data->data[0] >> 6) & 0x03);
    /* 报文第0个字节的低4、5位为类型 */
    pkt->header.type = ((((unsigned int)data->data[0] & 0x30) >> 4) & 0x03);
    /* 报文第0个字节的低4位为token长度 */
    pkt->header.tokenLen = (((unsigned int)data->data[0] & 0x0F));
    /* 报文第1个字节为操作码 */
    pkt->header.code = data->data[1];
    /* 报文第2、3个字节为msgId, 第2个字节为高8位, 第3个字节为低8位 */
    pkt->header.msgId = (unsigned short)((unsigned short)(data->data[2] << 8) | data->data[3]);

    return OLA_OK;
}

/* buf指向扩展字节前的一字节 */
static int OlaCoapParseOptionExtension(unsigned int *value, unsigned char *extendLen,
    const unsigned char *buf, unsigned int bufLen)
{
    if (*value == OLA_COAP_EXTEND_DELTA_VALUE_UINT8) {
        /* 包含1个字节的extend */
        *extendLen = 1;
        if (bufLen <= *extendLen) {
            OlaLogWarning("option extended delta or length overruns the buffer");
            return OLA_COAP_ERR_OPTION_TOO_SHORT;
        }

        /* 第1个字节为option的扩展字段 */
        *value = (unsigned int)(buf[1] + OLA_COAP_DELTA_UINT8_ADD_NUM);
        return OLA_OK;
    } else if (*value == OLA_COAP_EXTEND_DELTA_VALUE_UINT16) {
        /* 包含2个字节的extend */
        *extendLen = 2;
        if (bufLen <= *extendLen) {
            OlaLogWarning("option extended delta or length overruns the buffer");
            return OLA_COAP_ERR_OPTION_TOO_SHORT;
        }
        /* 第0个字节为option delta的高8位, 第1个字节为option delta的低8位 */
        *value = (unsigned int)(((buf[1] << 8) | buf[2]) + OLA_COAP_DELTA_UINT16_ADD_NUM);
        return OLA_OK;
    } else if (*value == OLA_COAP_EXTEND_DELTA_VALUE_UINT32) {
        /* 包含4个字节的extend */
        *extendLen = 4;
        if (bufLen <= *extendLen) {
            OlaLogWarning("option extended delta or length overruns the buffer");
            return OLA_COAP_ERR_OPTION_TOO_SHORT;
        }

        /* 第0个字节为option delta的高8位, 第3个字节为option delta的低8位 */
        unsigned int optionDeltaValue = (unsigned int)((buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4]);
        if (optionDeltaValue > (UINT32_MAX - OLA_COAP_DELTA_UINT32_ADD_NUM)) {
            OlaLogWarning("option extended delta or length value is out of range");
            return OLA_COAP_ERR_BAD_REQUEST;
        }

        *value = optionDeltaValue + OLA_COAP_DELTA_UINT32_ADD_NUM;
        return OLA_OK;
    }

    *extendLen = 0;
    return OLA_OK;
}

static int CoapParseHeaderForTcp(OlaCoapPacket *pkt, const OlaData *data, unsigned char *headerLen)
{
    /* header长度包括1字节报头+(0-4字节)扩展长度+1字节消息码 */
    if (data->len < 2) {
        OlaLogWarning("header len invalid");
        return OLA_COAP_ERR_HEADER_TOO_SHORT;
    }

    /* 报文第0个字节的高4位为报文长度 */
    pkt->header.len = (((unsigned int)data->data[0] >> 4) & 0x0F);
    /* 报文第0个字节的低4位为token长度 */
    pkt->header.tokenLen = (((unsigned int)data->data[0] & 0x0F));

    unsigned char extendByte;
    int ret;
    ret = OlaCoapParseOptionExtension(&pkt->header.len, &extendByte, data->data, data->len);
    if (ret != OLA_OK) {
        OlaLogWarning("parse extension error");
        return ret;
    }

    *headerLen = 2 + extendByte;
    if (data->len < *headerLen) {
        OlaLogWarning("header len invalid");
        return OLA_COAP_ERR_HEADER_TOO_SHORT;
    }

    /* 报文扩展长度后为操作码 */
    pkt->header.code = data->data[1 + extendByte];

    return OLA_OK;
}

static int CoapHeaderCheck(const OlaCoapPacket *pkt, const OlaData *data,
    unsigned char headerLen, OlaCoapFrameType type)
{
    if (type == OLA_COAP_FRAME_TYPE_UNRELIABLE) {
        if (pkt->header.ver != OLA_COAP_VERSION) {
            OlaLogWarning("invalid version");
            return OLA_COAP_ERR_VERSION_INVALID;
        }
    }

    if (pkt->header.tokenLen > OLA_COAP_TOKEN_MAX_LEN) {
        OlaLogWarning("token length too long");
        return OLA_COAP_ERR_INVALID_TOKEN_LEN;
    }

    if ((data->len > headerLen) && (pkt->header.code == 0)) {
        OlaLogWarning("empty message");
        return OLA_COAP_ERR_INVALID_EMPTY_MESSAGE;
    }
    return OLA_OK;
}

static int CoapParseToken(OlaCoapPacket *pkt, const OlaData *data, unsigned char headerLen)
{
    if (pkt->header.tokenLen == 0) {
        pkt->token.data = NULL;
        pkt->token.len = 0;
    } else if ((unsigned int)(pkt->header.tokenLen + headerLen) > data->len) {
        OlaLogWarning("token length overruns the buffer");
        return OLA_COAP_ERR_INVALID_TOKEN_LEN;
    } else {
        pkt->token.data = &data->data[headerLen];
        pkt->token.len = pkt->header.tokenLen;
    }

    return OLA_OK;
}

static int OlaCoapParseOption(OlaCoapOption *option, unsigned short *runningDelta, const unsigned char **buf,
    unsigned int bufLen)
{
    const unsigned char *dataPos = NULL;
    unsigned short len;
    unsigned short delta;
    int ret;

    /* option最小长度为1 */
    if (bufLen < 1)  {
        OlaLogWarning("option header overruns the buffer");
        return OLA_COAP_ERR_OPTION_TOO_SHORT;
    }
    dataPos = *buf;

    /* 第0字节包含option delta和option length, 高4位为option delta, 低4位为option length */
    delta = (dataPos[0] & 0xF0) >> 4;
    len = dataPos[0] & 0x0F;

    unsigned int value = delta;
    unsigned char extendByte;
    ret = OlaCoapParseOptionExtension(&value, &extendByte, dataPos, bufLen);
    if ((ret != OLA_OK) || (value > UINT16_MAX)) {
        OlaLogWarning("Invalid Option delta");
        return ret;
    }
    delta = value;
    /* 已经校验过长度保证不越界 */
    dataPos += extendByte;

    value = len;
    ret = OlaCoapParseOptionExtension(&value, &extendByte, dataPos, bufLen - extendByte);
    if ((ret != OLA_OK) || (value > UINT16_MAX)) {
        OlaLogWarning("Invalid Option length");
        return ret;
    }
    len = value;
    dataPos += extendByte;

    /* 加1之后的位置为Option信息 */
    if ((dataPos + 1 + len) > (*buf + bufLen)) {
        OlaLogWarning("option too big than buffer");
        return OLA_COAP_ERR_OPTION_TOO_BIG;
    }

    option->option = (unsigned short)(delta + *runningDelta);
    /* 加1之后的位置为Option信息 */
    option->buf.data = (unsigned char *)(dataPos + 1);
    option->buf.len = len;

    *buf = dataPos + 1 + len;
    *runningDelta = (unsigned short)(*runningDelta + delta);
    return OLA_OK;
}

static int CoapParseOptionsAndPayload(OlaCoapPacket *pkt, const OlaData *data, unsigned char headerLen)
{
    unsigned char optionIndex = 0;
    unsigned short delta = 0;
    const unsigned char *dataPos = data->data + headerLen + pkt->header.tokenLen;
    const unsigned char *end = data->data + data->len;
    int ret;

    /* 解析每一个option, 0xFF为payload标识符 */
    while ((dataPos < end) && (*dataPos != 0xFF) && (optionIndex < OLA_COAP_OPTION_MAX_NUM)) {
        ret = OlaCoapParseOption(&((pkt->options)[optionIndex]), &delta, &dataPos, end - dataPos);
        if (ret != OLA_OK) {
            return ret;
        }
        optionIndex++;
    }

    /* option达到上限 */
    if ((dataPos < end) && (*dataPos != 0xFF) && (optionIndex >= OLA_COAP_OPTION_MAX_NUM)) {
        OlaLogWarning("options greater than maximum limit");
        return OLA_COAP_ERR_SERVER_ERROR;
    }
    pkt->optionsNum = optionIndex;

    /* 当前已读完数据或者没有payload标识则解析结束 */
    if ((dataPos >= end) || (*dataPos != 0xFF)) {
        pkt->payload.data = NULL;
        pkt->payload.len = 0;
        return OLA_OK;
    }

    /* 加1后到达缓存结束, 说明不存在payload */
    if (dataPos + 1 >= end) {
        OlaLogWarning("pkt have payload maker and no payload");
        return OLA_COAP_ERR_INVALID_PACKET;
    }

    /* 加1之后的位置为payload信息 */
    pkt->payload.data = (unsigned char *)(dataPos + 1);
    pkt->payload.len = (unsigned int)(end - (dataPos + 1));
    return OLA_OK;
}

int OlaCoapDecode(OlaCoapPacket *pkt, const OlaData *data, OlaCoapFrameType type)
{
    if ((pkt == NULL) || (data == NULL) || (data->len == 0) || (data->data == NULL)) {
        OlaLogWarning("invalid param");
        return OLA_COAP_ERR_INVALID_ARGUMENT;
    }

    int ret;
    unsigned char headerLen = OLA_COAP_UDP_HEADER_LEN;
    if (type == OLA_COAP_FRAME_TYPE_UNRELIABLE) {
        ret = CoapParseHeaderForUdp(pkt, data);
    } else if (type == OLA_COAP_FRAME_TYPE_RELIABLE) {
        ret = CoapParseHeaderForTcp(pkt, data, &headerLen);
    } else {
        OlaLogWarning("type invalid");
        return OLA_COAP_ERR_INVALID_ARGUMENT;
    }
    pkt->type = type;
    if (ret != OLA_OK) {
        OlaLogWarning("parse header error");
        return ret;
    }

    ret = CoapHeaderCheck(pkt, data, headerLen, type);
    if (ret != OLA_OK) {
        OlaLogWarning("header invalid");
        return ret;
    }

    ret = CoapParseToken(pkt, data, headerLen);
    if (ret != OLA_OK) {
        OlaLogWarning("parse token error");
        return ret;
    }

    ret = CoapParseOptionsAndPayload(pkt, data, headerLen);
    if (ret != OLA_OK) {
        OlaLogWarning("parse options or payload error");
        return ret;
    }

    OlaLogInfo("coap decode completed");
    return OLA_OK;
}

static int CoapCreateHeaderForUdp(OlaCoapPacket *pkt, const OlaCoapPacketParam *param, OlaBuffer *buf)
{
    if (param->msgType > OLA_COAP_MSG_TYPE_RESET) {
        OlaLogWarning("invalid type");
        return OLA_COAP_ERR_UNKNOWN_MSG_TYPE;
    }

    if (buf->size < (OLA_COAP_UDP_HEADER_LEN + param->tokenLen)) {
        OlaLogWarning("buffer too small");
        return OLA_COAP_ERR_PACKET_EXCEED_MAX_PDU;
    }

    if ((param->code == 0) &&
        ((param->tokenLen != 0) || (param->payload.len != 0) || (param->optionsNum != 0))) {
        OlaLogWarning("invalid empty msg");
        return OLA_COAP_ERR_INVALID_EMPTY_MESSAGE;
    }

    pkt->header.type = (unsigned char)param->msgType & 0x03;
    pkt->header.ver = OLA_COAP_VERSION;
    pkt->header.code = param->code;
    pkt->header.msgId = param->msgId;
    pkt->header.tokenLen = param->tokenLen;

    /* 第0个字节的高2位为版本号, 第4、5位为消息类型, 低4位为token长度 */
    buf->buf[0] = ((unsigned char)(pkt->header.ver << 6) | (unsigned char)(pkt->header.type << 4) |
        (unsigned char)param->tokenLen);

    /* 第1个字节为操作码 */
    buf->buf[1] = (unsigned char)pkt->header.code;

    /* 第2个字节为msgId的低字节, 第3个字节为msgId的高字节 */
    buf->buf[2] = (unsigned char)((pkt->header.msgId & 0xFF00) >> 8);
    buf->buf[3] = (unsigned char)(pkt->header.msgId & 0x00FF);

    /* 第4字节开始为token */
    if (param->tokenLen != 0) {
        if (memcpy_s(&buf->buf[4], buf->size - 4, param->token, param->tokenLen) != EOK) {
            OlaLogError("memcpy error");
            return OLA_SECUREC_ERR_MEMCOPY;
        }
        pkt->token.data = &buf->buf[4];
        pkt->token.len = param->tokenLen;
    }

    buf->len = OLA_COAP_UDP_HEADER_LEN + param->tokenLen;
    return OLA_OK;
}

static void CoapGetOptionParam(unsigned int value, unsigned char *param)
{
    /* option delta的值为0-12, 直接取值低4字节 */
    if (value < OLA_COAP_DELTA_UINT8_ADD_NUM) {
        *param = (unsigned char)(value & 0xFF);
        return;
    }

    /* option的值大于12, 小于等于13 + 0xFF, 需要使用扩展字13 */
    if (value <= OLA_COAP_DELTA_UINT16_ADD_NUM) {
        *param = OLA_COAP_EXTEND_DELTA_VALUE_UINT8;
        return;
    }

    /* option的值大于13 + 0xFF, 小于等于269 + 0xFFFF, 需要使用扩展字14 */
    if (value <= OLA_COAP_DELTA_UINT32_ADD_NUM) {
        *param = OLA_COAP_EXTEND_DELTA_VALUE_UINT16;
        return;
    }
    /* option的值大于269 + 0xFFFF, 需要使用扩展字节15 */
    *param = OLA_COAP_EXTEND_DELTA_VALUE_UINT32;

    return;
}

static unsigned short CoapGetOptionLength(const OlaCoapOption *option, unsigned short optionDelta)
{
    /* 1字节包含option delta和option length */
    unsigned short optionLen = 1;
    unsigned char delta = 0;

    CoapGetOptionParam(optionDelta, &delta);
    if (delta == OLA_COAP_EXTEND_DELTA_VALUE_UINT8) {
        optionLen += 1;
    } else if (delta == OLA_COAP_EXTEND_DELTA_VALUE_UINT16) {
        optionLen += 2;
    }

    CoapGetOptionParam(option->buf.len, &delta);
    if (delta == OLA_COAP_EXTEND_DELTA_VALUE_UINT8) {
        optionLen += 1;
    } else if (delta == OLA_COAP_EXTEND_DELTA_VALUE_UINT16) {
        optionLen += 2;
    }

    return optionLen + option->buf.len;
}

static int CoapCheckOption(const OlaCoapPacket *pkt, const OlaCoapOption *option, const OlaBuffer *buf)
{
    if ((option->buf.data == NULL) || (option->buf.len == 0)) {
        OlaLogWarning("option invalid");
        return OLA_COAP_ERR_INVALID_ARGUMENT;
    }

    if (pkt->optionsNum >= OLA_COAP_OPTION_MAX_NUM) {
        OlaLogWarning("invalid option num");
        return OLA_COAP_ERR_OPTION_NUM_INVALID;
    }

    unsigned short optionDelta = option->option;
    if (pkt->optionsNum != 0) {
        if (option->option < pkt->options[pkt->optionsNum - 1].option)  {
            OlaLogWarning("options out of order");
            return OLA_COAP_ERR_OPTION_DELTA_INVALID;
        }
        optionDelta -= pkt->options[pkt->optionsNum - 1].option;
    }

    unsigned short optionLen = CoapGetOptionLength(option, optionDelta);
    if (buf->len + optionLen > buf->size) {
        OlaLogWarning("option overruns the buffer size");
        return OLA_COAP_ERR_PACKET_EXCEED_MAX_PDU;
    }

    return OLA_OK;
}

static int CoapAddOption(OlaCoapPacket *pkt, const OlaCoapOption *option, OlaBuffer *buf)
{
    int ret;
    ret = CoapCheckOption(pkt, option, buf);
    if (ret != OLA_OK) {
        OlaLogWarning("option invalid");
        return ret;
    }

    unsigned short optionDelta = option->option;
    if (pkt->optionsNum != 0) {
        optionDelta -= pkt->options[pkt->optionsNum - 1].option;
    }

    unsigned char delta;
    unsigned char len;
    CoapGetOptionParam(optionDelta, &delta);
    CoapGetOptionParam(option->buf.len, &len);

    /* option第1个字节, 前4位为option delta, 后4位为option长度 */
    buf->buf[buf->len++] = (unsigned char)((delta << 4) | len);

    /* 填入扩展option delta字段 */
    if (delta == OLA_COAP_EXTEND_DELTA_VALUE_UINT8) {
        buf->buf[buf->len++] = (unsigned char)(optionDelta - OLA_COAP_DELTA_UINT8_ADD_NUM);
    } else if (delta == OLA_COAP_EXTEND_DELTA_VALUE_UINT16) {
        /* 先保存option长度的高8位, 后保存option长度的低8位 */
        buf->buf[buf->len++] = (unsigned char)((optionDelta - OLA_COAP_DELTA_UINT16_ADD_NUM) >> 8);
        buf->buf[buf->len++] = (unsigned char)((optionDelta - OLA_COAP_DELTA_UINT16_ADD_NUM) & 0xFF);
    }

    /* 填入扩展option长度 */
    if (len == OLA_COAP_EXTEND_DELTA_VALUE_UINT8) {
        buf->buf[buf->len++] = (unsigned char)(option->buf.len - OLA_COAP_DELTA_UINT8_ADD_NUM);
    } else if (len == OLA_COAP_EXTEND_DELTA_VALUE_UINT16) {
        /* 高8位在前, 低8位在后 */
        buf->buf[buf->len++] = (unsigned char)((option->buf.len - OLA_COAP_DELTA_UINT16_ADD_NUM) >> 8);
        buf->buf[buf->len++] = (unsigned char)((option->buf.len - OLA_COAP_DELTA_UINT16_ADD_NUM) & 0xFF);
    }

    /* 填入option内容 */
    if (option->buf.len != 0) {
        if (memcpy_s(&buf->buf[buf->len], buf->size - buf->len, option->buf.data, option->buf.len) != EOK) {
            OlaLogError("memcpy error");
            return OLA_SECUREC_ERR_MEMCOPY;
        }
    }

    pkt->options[pkt->optionsNum].buf.data = &buf->buf[buf->len];
    pkt->options[pkt->optionsNum].buf.len = option->buf.len;
    pkt->options[pkt->optionsNum].option = option->option;
    buf->len += option->buf.len;
    pkt->optionsNum++;

    return OLA_OK;
}

static int CoapAddData(OlaCoapPacket *pkt, const OlaCoapPacketParam *param, OlaBuffer *buf)
{
    /* 需要额外1字节存储payload标志位 */
    if ((param->payload.len > UINT16_MAX) || (param->payload.len >= buf->size - buf->len)) {
        OlaLogWarning("payload overruns the buffer");
        return OLA_COAP_ERR_PACKET_EXCEED_MAX_PDU;
    }

    pkt->payload.len = param->payload.len;
    buf->buf[buf->len++] = 0xFF;
    if (memcpy_s(&buf->buf[buf->len], buf->size - buf->len, param->payload.data, param->payload.len) != EOK) {
        OlaLogError("memcpy error");
        return OLA_SECUREC_ERR_MEMCOPY;
    }
    pkt->payload.data = &buf->buf[buf->len];
    buf->len += param->payload.len;

    return OLA_OK;
}

static int CoapCreateBody(OlaCoapPacket *pkt, const OlaCoapPacketParam *param, OlaBuffer *buf)
{
    int ret;
    if (param->optionsNum != 0) {
        if (param->optionsNum > OLA_COAP_OPTION_MAX_NUM) {
            OlaLogWarning("option num invalid");
            return OLA_COAP_ERR_OPTION_NUM_INVALID;
        }
        for (unsigned int i = 0; i < param->optionsNum; ++i) {
            ret = CoapAddOption(pkt, &param->options[i], buf);
            if (ret != OLA_OK) {
                OlaLogWarning("add option error");
                return ret;
            }
        }
    }

    if (param->payload.len != 0) {
        ret = CoapAddData(pkt, param, buf);
        if (ret != OLA_OK) {
            OlaLogWarning("add data error");
            return ret;
        }
    }

    return OLA_OK;
}

static int GetCoapBodyLen(const OlaCoapPacketParam *param, unsigned int *bodyLen)
{
    *bodyLen = 0;
    unsigned short preOption = 0;
    for (unsigned int i = 0; i < param->optionsNum; ++i) {
        if (param->options[i].option < preOption) {
            OlaLogWarning("options out of order");
            return OLA_COAP_ERR_OPTION_DELTA_INVALID;
        }
        *bodyLen += CoapGetOptionLength(&param->options[i], param->options[i].option - preOption);
        preOption = param->options[i].option;
    }

    if (param->payload.len != 0) {
        /* 包含0xFF分割符 */
        *bodyLen += 1 + param->payload.len;
    }
    return OLA_OK;
}

static int CoapCreateHeaderForTcp(OlaCoapPacket *pkt, const OlaCoapPacketParam *param, OlaBuffer *buf)
{
    unsigned char len;
    unsigned char header[OLA_COAP_TCP_HEADER_MAX_LEN] = { 0 };
    unsigned char headerLen = 0;
    unsigned int bodyLen;

    pkt->header.code = param->code;
    pkt->header.tokenLen = param->tokenLen;
    int ret = GetCoapBodyLen(param, &bodyLen);
    if (ret != OLA_OK) {
        OlaLogWarning("get body len error");
        return ret;
    }

    CoapGetOptionParam(bodyLen, &len);
    pkt->header.len = bodyLen;
    /* 第0个字节的高4位为报文长度, 低4位为token长度 */
    header[headerLen++] = ((unsigned char)(len << 4) | (unsigned char)param->tokenLen);

    /* 扩展长度高位在前低位在后 */
    if (len == OLA_COAP_EXTEND_DELTA_VALUE_UINT8) {
        header[headerLen++] = (unsigned char)(bodyLen - OLA_COAP_DELTA_UINT8_ADD_NUM);
    } else if (len == OLA_COAP_EXTEND_DELTA_VALUE_UINT16) {
        header[headerLen++] = (unsigned char)((bodyLen - OLA_COAP_DELTA_UINT16_ADD_NUM) >> 8);
        header[headerLen++] = (unsigned char)((bodyLen - OLA_COAP_DELTA_UINT16_ADD_NUM) & 0xFF);
    } else if (len == OLA_COAP_EXTEND_DELTA_VALUE_UINT32) {
        header[headerLen++] = (unsigned char)(((bodyLen - OLA_COAP_DELTA_UINT32_ADD_NUM) >> 24) & 0xFF);
        header[headerLen++] = (unsigned char)(((bodyLen - OLA_COAP_DELTA_UINT32_ADD_NUM) >> 16) & 0xFF);
        header[headerLen++] = (unsigned char)(((bodyLen - OLA_COAP_DELTA_UINT32_ADD_NUM) >> 8) & 0xFF);
        header[headerLen++] = (unsigned char)((bodyLen - OLA_COAP_DELTA_UINT32_ADD_NUM) & 0xFF);
    }

    /* 扩展长度后为消息码 */
    header[headerLen++] = (unsigned char)pkt->header.code;

    if (buf->size < (headerLen + param->tokenLen)) {
        OlaLogWarning("buffer too small");
        return OLA_COAP_ERR_PACKET_EXCEED_MAX_PDU;
    }

    if ((param->code == 0) &&
        ((param->tokenLen != 0) || (param->payload.len != 0) || (param->optionsNum != 0))) {
        OlaLogWarning("invalid empty msg");
        return OLA_COAP_ERR_INVALID_EMPTY_MESSAGE;
    }

    if (memcpy_s(buf->buf, buf->size, header, headerLen) != EOK) {
        OlaLogError("memcpy error");
        return OLA_SECUREC_ERR_MEMCOPY;
    }
    buf->len += headerLen;

    if (param->tokenLen != 0) {
        if (memcpy_s(&buf->buf[buf->len], buf->size - buf->len, param->token, param->tokenLen) != EOK) {
            OlaLogError("memcpy error");
            return OLA_SECUREC_ERR_MEMCOPY;
        }
        pkt->token.data = &buf->buf[buf->len];
        pkt->token.len = param->tokenLen;
        buf->len += param->tokenLen;
    }

    return OLA_OK;
}

int OlaCoapEncode(OlaCoapPacket *pkt, const OlaCoapPacketParam *param, OlaBuffer *buf)
{
    if ((pkt == NULL) || (param == NULL) || (buf == NULL) || (buf->size == 0) ||
        (buf->buf == NULL) || (buf->len != 0) || (param->frameType > OLA_COAP_FRAME_TYPE_RELIABLE) ||
        (param->tokenLen > OLA_COAP_TOKEN_MAX_LEN) ||
        ((param->payload.len != 0) && (param->payload.data == NULL))) {
        OlaLogWarning("invalid param");
        return OLA_COAP_ERR_INVALID_ARGUMENT;
    }
    int ret = OLA_COM_ERR_NOT_SUPPORT;

    /* udp报文报头长度固定，tcp报文报头长度取决于body长度 */
    if (param->frameType == OLA_COAP_FRAME_TYPE_UNRELIABLE) {
        ret = CoapCreateHeaderForUdp(pkt, param, buf);
    } else if (param->frameType == OLA_COAP_FRAME_TYPE_RELIABLE) {
        ret = CoapCreateHeaderForTcp(pkt, param, buf);
    }
    if (ret != OLA_OK) {
        OlaLogWarning("create coap header error");
        return ret;
    }
    pkt->type = param->frameType;

    ret = CoapCreateBody(pkt, param, buf);
    if (ret != OLA_OK) {
        OlaLogWarning("create coap body error");
        return ret;
    }

    OlaLogInfo("coap encode completed");
    return OLA_OK;
}

static int CoapGetHeaderLen(const OlaCoapPacketParam *param, unsigned int *headerLen, unsigned int bodyLen)
{
    if (param->frameType == OLA_COAP_FRAME_TYPE_UNRELIABLE) {
        *headerLen = OLA_COAP_UDP_HEADER_LEN + param->tokenLen;
        return OLA_OK;
    }
    if (param->frameType == OLA_COAP_FRAME_TYPE_RELIABLE) {
        unsigned char len;
        CoapGetOptionParam(bodyLen, &len);
        /* 第1字节 + 请求码1字节 + token长度 */
        *headerLen = 2 + param->tokenLen;
        /* 报文长度扩展字节 */
        if (len == OLA_COAP_EXTEND_DELTA_VALUE_UINT8) {
            *headerLen += 1;
        } else if (len == OLA_COAP_EXTEND_DELTA_VALUE_UINT16) {
            *headerLen += 2;
        } else if (len == OLA_COAP_EXTEND_DELTA_VALUE_UINT32) {
            *headerLen += 4;
        }
        return OLA_OK;
    }
    return OLA_OK;
}

int OlaCoapGetSize(const OlaCoapPacketParam *param, unsigned int *size)
{
    if ((param == NULL) || (size == NULL)) {
        OlaLogWarning("invalid param");
        return OLA_COAP_ERR_INVALID_ARGUMENT;
    }

    int ret;
    unsigned int bodyLen = 0;
    unsigned int headerLen = 0;
    ret = GetCoapBodyLen(param, &bodyLen);
    if (ret != OLA_OK) {
        OlaLogWarning("get body len error");
        return ret;
    }

    ret = CoapGetHeaderLen(param, &headerLen, bodyLen);
    if (ret != OLA_OK) {
        OlaLogWarning("get header len error");
        return ret;
    }

    if (bodyLen > UINT32_MAX - headerLen) {
        OlaLogWarning("packet too big");
        return OLA_COM_ERR_UINT32_OVERFLOW;
    }
    *size = bodyLen + headerLen;
    return OLA_OK;
}

int OlaCoapGetUri(const OlaCoapPacket *pkt, OlaBuffer *buf)
{
    if ((pkt == NULL) || (buf == NULL) || (pkt->optionsNum == 0) ||
        (buf->len != 0) || (buf->size == 0) || (buf->buf == NULL)) {
        OlaLogWarning("invalid param");
        return OLA_COAP_ERR_INVALID_ARGUMENT;
    }

    bool isFirst = true;
    for (unsigned int i = 0; i < pkt->optionsNum; i++) {
        if (pkt->options[i].option == OLA_COAP_OPTION_TYPE_URI_PATH) {
            if (isFirst) {
                isFirst = false;
            } else {
                /* 多个URI之间用"/"分割 */
                if (buf->len + 1 > buf->size) {
                    OlaLogWarning("buf overflow");
                    return OLA_COM_ERR_EXCEED_MAX_BUF_SIZE;
                }
                buf->buf[buf->len++] = '/';
            }
            if (buf->len + pkt->options[i].buf.len > buf->size) {
                OlaLogWarning("buf overflow");
                return OLA_COM_ERR_EXCEED_MAX_BUF_SIZE;
            }
            if (memcpy_s(&buf->buf[buf->len], buf->size - buf->len,
                pkt->options[i].buf.data, pkt->options[i].buf.len) != EOK) {
                OlaLogError("memcpy error");
                return OLA_SECUREC_ERR_MEMCOPY;
            }
            buf->len += pkt->options[i].buf.len;
        }
    }
    if (isFirst) {
        OlaLogWarning("no option");
        return OLA_COAP_ERR_OPTION_NO_URI;
    }

    return OLA_OK;
}

#ifdef OLA_COAP_DEBUG
static void CoapDumpHeader(const OlaCoapPacket *pkt)
{
    OlaPrintf("Header:\r\n");
    if (pkt->type == OLA_COAP_FRAME_TYPE_RELIABLE) {
        OlaPrintf("    len   0x%X\r\n", pkt->header.len);
    } else {
        OlaPrintf("    ver   0x%02X\r\n", pkt->header.ver);
        OlaPrintf("    type  0x%02X\r\n", pkt->header.type);
        OlaPrintf("    msgid 0x%X\r\n", pkt->header.msgId);
    }
    OlaPrintf("    tkl   0x%02X\r\n", pkt->header.tokenLen);
    OlaPrintf("    code  0x%02X\r\n", pkt->header.code);
}

static void CoapDumpToken(const OlaCoapPacket *pkt)
{
    if ((pkt->token.data == NULL) || (pkt->token.len == 0) ||
        (pkt->token.len > OLA_COAP_TOKEN_MAX_LEN)) {
        return;
    }
    OlaPrintf("Token:    0x");
    OlaDumpHex(pkt->token.data, pkt->token.len);
    OlaPrintf("\r\n");
}

static void CoapDumpOption(const OlaCoapPacket *pkt)
{
    if (pkt->optionsNum == 0) {
        return;
    }
    OlaPrintf("Option:\r\n");
    for (unsigned int i = 0; i < pkt->optionsNum; ++i) {
        OlaPrintf("          0x%X\r\n", pkt->options[i].option);
        OlaPrintf("          ");
        OlaDumpChar((const char *)pkt->options[i].buf.data, pkt->options[i].buf.len);
        OlaPrintf("\r\n");
    }
}

static void CoapDumpPayload(const OlaCoapPacket *pkt)
{
    if ((pkt->payload.data == NULL) || (pkt->payload.len == 0)) {
        return;
    }
    OlaPrintf("Payload:\r\n");
    OlaDumpChar((const char *)pkt->payload.data, pkt->payload.len);
    OlaPrintf("\r\n");
}
#endif

void OlaCoapDumpPacket(const OlaCoapPacket *pkt)
{
#ifdef OLA_COAP_DEBUG
    if (pkt == NULL) {
        return;
    }
    CoapDumpHeader(pkt);
    CoapDumpToken(pkt);
    CoapDumpOption(pkt);
    CoapDumpPayload(pkt);
#else
    (void)pkt;
#endif
}

int OlaGetTcpCoapPacketSizeByHeader(const unsigned char *header, unsigned int len, unsigned int *size)
{
    if ((header == NULL) || (len == 0) || (size == NULL)) {
        OlaLogWarning("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    /* 报文第0个字节的高4位为报文长度 */
    *size = ((header[0] >> 4) & 0x0F);
    /* 报文第0个字节的低4位为token长度 */
    unsigned char tokenLen = ((header[0] & 0x0F));

    unsigned char extendByte;
    int ret;
    ret = OlaCoapParseOptionExtension(size, &extendByte, header, len);
    if (ret != OLA_OK) {
        OlaLogWarning("parse extension error");
        return ret;
    }
    /* 报文大小再加上消息码1字节、扩展长度、token长度、首字节 */
    if (*size > (UINT32_MAX - 2 - extendByte - tokenLen)) {
        OlaLogWarning("coap size out of range");
        return OLA_COAP_ERR_BAD_REQUEST;
    }
    *size += (2 + extendByte + tokenLen);
    return OLA_OK;
}